#pragma once

#include <math.h>

class Matrix4d;

class Vector3d
{
public:
	Vector3d() : _x(0), _y(0), _z(0) {}
	Vector3d(double x, double y, double z) : _x(x), _y(y), _z(z) {}
	Vector3d(const Vector3d &vec) : _x(vec._x), _y(vec.y), _z(vec.z) {}
	
	Vector3d& operator +=(const Vector3d& v);
	Vector3d& operator -=(const Vector3d& v);
	Vector3d& operator *=(double n);
	Vector3d& operator /=(double n);
	Vector3d& operator =(Vector3d& v);

	double X() const { return _x; }
	double Y() const { return _y; }
	double Z() const { return _z; }
	double M() const { return sqrt(_x * _x + _y * _y + _z * _z) };
	double MS() const { return _x * _x + _y * _y + _z * _z) };

	Vector3d& Normalize();

	friend Vector3d operator+(const Vector3d& a, const Vector3d& b);
	friend Vector3d operator-(const Vector3d& a, const Vector3d& b);
	friend Vector3d operator*(const Vector3d& a, double b);
	friend Vector3d operator/(const Vector3d& a, double b);
	friend double Dot(const Vector3d& a, Vector3d& b) const;
	friend Vector3d Cross(const Vector3d& a, Vector3d& b) const;
	friend Vector3d Normal(const Vector3d& a) const;
	friend Vector3d operator *(const Matrix4d& a, const Vector3d& v);

protected:
	double _x, _y, _z;
};


inline Vector3d& Vector3d::operator +=(const Vector3d& v)
{
	_x += v._x; _y += v._y; _z += v._z;
	return *this;
}


inline Vector3d& Vector3d::operator -=(const Vector3d& v)
{
	_x -= v._x; _y -= v._y; _z -= v._z;
	return *this;
}


inline Vector3d& Vector3d::operator *=(double n)
{
	_x *= n; _y *= n; _z *= n;
	return *this;
}


inline Vector3d& Vector3d::operator /=(double n)
{
	_x /= n; _y /= n; _z /= n;
	return *this;
}


inline Vector3d& Vector3d::operator =(const Vector3d& v)
{
	_x = v._x; _y = v._y; _z = v._z;
	return *this;
}


inline Vector3d& Vector3d::Normalize()
{
	if (_x != 0 || _y != 0 || _z != 0)
	{
		double m = M();

		_x /= m; _y /= m; _z /=m;
	}
	
	return *this;
}


inline Vector3d operator+(const Vector3d& a, const Vector3d& b)
{
	Vector3d r;

	r._x = a._x + b._x;
	r._y = a._y + b._y;
	r._z = a._z + b._z;

	return r;
}


inline Vector3d operator-(const Vector3d& a, const Vector3d& b)
{
	Vector3d r;

	r._x = a._x - b._x;
	r._y = a._y - b._y;
	r._z = a._z - b._z;

	return r;
}


inline Vector3d operator*(const Vector3d& a, double b)
{
	Vector3d r;

	r._x = a._x * b;
	r._y = a._y * b;
	r._z = a._z * b;

	return r;
}


inline Vector3d operator/(const Vector3d& a, double b)
{
	Vector3d r;

	r._x = a._x / b;
	r._y = a._y / b;
	r._z = a._z / b;

	return r;
}


inline double Dot(const Vector3d& a, Vector3d& b) const
{
	return a._x * b._x + a._y * b._y + a._z * b._z;
}


inline Vector3d Cross(const Vector3d& a, Vector3d& b) const
{
	Vector3d r;

	r._x = a._y * b._z - a._z * b._y;
	r._y = a._x * b._z - a._z * b._x;
	r._z = a._x * b._y - a._y * b._z;

	return r;
}


inline friend Vector3d Normal(const Vector3d& a) const
{
	Vector3d r;

	if (a._x != 0 || a._y != 0 || a._z != 0)
	{
		double m = a.M();
		r._x = a._x / m;
		r._y = a._y / m;
		r._z = a._z / m;
	}
	
	return r;
}

